/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/


const PENDING = 'pending' //  等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

/**
 * @description 处理then方法回调函数返回值, 如果是普通值，直接调用resolve方法； 如果是Promise对象，根据结果决定调用resolve ｜ reject; 如果Promise对象是当前then方法返回的Promise对象，则抛类型异常
 * @params { Any } cbVaue  可能是常量，也可能是一个promise 对象
 * @params { MyPromise } newThenPromise then 方法中返回的new Promise，then方法不能返回当前的then自身
 * @params { Function } resolve 成功的回调函数
 * @params { Function } reject 失败的回调函数
 * 
 */
function resolvePromise(cbVaue, newThenPromise, resolve, reject) {
	if(cbVaue === newThenPromise) {
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	}


	if(cbVaue instanceof MyPromise) {
		// cbVaue.then(x => resolve(x), reason => reject(reason))
		cbVaue.then(resolve, reject)
	} else {
		resolve(cbVaue)
	}
}



class MyPromise {
	//  Promise是一个类，在执行这个类的时候，需要传递一个执行器，执行器会立即执行;
	constructor (executor) {
		try {
			executor(this.resolve, this.reject)
		} catch(e) {
			// 捕获执行器中的异常，传递到then方法onRejected
			this.reject(e)
		}
	}


	/**
	 * Promise 中有三种状态， pending(等待)、fulfilled(成功)、rejected(失败)
	 **/

	status = PENDING


	/**
	 * promise 状态为FULFILLED 时，需要缓存其值，供内部调用
	 **/

	resolvedValue = null


	/**
	 * promise 状态为REJECTED 时，需要缓存其原因， 供内部调用
	 **/

	rejectedReason = null


	/**
	 * 同一个promise可以多次调用then方法，当executor中resolve为异步调用时，需要收集promise.then中的onFulfilled
	 **/

	asyncResolveCallbackbs = []


	/**
	 * 同一个promise可以多次调用then方法，当executor中reject为异步调用时，需要收集promise.then中的onRejected
	 **/

	asyncRejectCallbacks = []


	/**
	 * 
	 * 类型：实例方法-resolve
	 * 作用：
	 * 1. 将status状态从PENDING 改为 FULFILLED
	 * 2. 缓存成功之后的值
	 * 3. resolve为异步回调时，需要立即执行asyncResolveCb
	 *
	 ***/
	resolve = value => {
		if(this.status !== PENDING) return


		// constructor init params
		this.status = FULFILLED
		this.resolvedValue = value

		// async resolve
		while(this.asyncResolveCallbackbs.length) {
			const curAsyncResolveCb = this.asyncResolveCallbackbs.shift()

			curAsyncResolveCb()
		}
	}

	/**
	 * 类型：实例方法-reject
	 * 作用： 
	 * 1. 将status状态从PENDING 改为 REJECTED
	 * 2. 缓存失败之后的原因
	 *
	 ***/
	reject = reason => {
		if(this.status !== PENDING) return

		// constructor init params
		this.status = REJECTED
		this.rejectedReason = reason

		// async reject
		while(this.asyncRejectCallbacks.length) {
			const curAsyncRejectCb = this.asyncRejectCallbacks.shift()

			curAsyncRejectCb()
		}

	}


	/**
	 * 类型：prototype方法
	 *
	 * 参数：
	 * onFulfilled：可选； 
	 * 1. 当 status 为 FULFILLED 时调用的函数。该函数有一个参数，即接受的最终结果。 
	 * 2. 如果该参数不是函数，则会在内部被替换为 (x) => x，即原样返回 promise 最终结果的函数
	 * onRejected： 可选：
	 * 1. 当 status 为 REJECTED 时调用的函数。该函数有一个参数，即拒绝的原因。
	 * 2. 如果该参数不是函数，则会在内部被替换为一个 "Thrower" 函数
	 *
	 *
	 * 特性：
	 * 1. 同一个promise可以调用多次then方法，
	 * 例如： const exePromise = new MyPromise(fn); 
	 * exePromise.then(console.log, console.log)
	 * exrPromise.then(console.log, console.log)
	 * ....
	 * 2. then方法可以实现链式调用，每一个then方法返回的都是一个新的Promise对象。同时，后一个then方法接收的是前一个then方法的回调函数的返回值；
	 * 3. then方法不能返回当前的then自身，否则会报：TypeError：Chaining cycle detected for promise #<Promise>
	 * 
	 * 
	 ***/
	then(onFulfilled, onRejected) {
		// callback is null
		onFulfilled = onFulfilled ? onFulfilled : x => x
		onRejected = onRejected ? onRejected : reason => { throw reason }

		const newThenPromise =  new MyPromise((resolve, reject) => {
			// 外层执行作用域为同步任务，执行器为立即执行函数；开启宏任务获取外层作用域的newThenPromise对象
			setTimeout(() => {
				// 集中捕获then回调中抛出的异常，并传递给下一个then方法
				const catchThenException = (callbackFunc, cacheValue) => {
					try {
						const callbackFuncValue = callbackFunc(cacheValue)
						// 链式调用，上一个返回值可能为常规值，也可能为promise对象
						callbackFuncValue && resolvePromise(callbackFuncValue, newThenPromise, resolve, reject)	
					} catch (e) {
						reject(e)
					}
				}

				// console.log('____this.status', this.status)

				if(this.status === FULFILLED) {
					// onFulfilled(this.resolvedValue)
					catchThenException(onFulfilled, this.resolvedValue)
				} else if(this.status === REJECTED) {
					// onRejected(this.rejectedReason)
					catchThenException(onRejected, this.rejectedReason)
				} else {
					// 同步
					// this.asyncResolveCb = resolvedCb
					// this.asyncRejectCb = rejectedCb
					// 同步 ｜ 异步
					// this.asyncResolveCallbackbs.push(onFulfilled)
					// this.asyncRejectCallbacks.push(onRejected)

					// 同步 ｜ 异步 ｜ 链式调用 => 收集成功回调
					this.asyncResolveCallbackbs.push(() => {
						catchThenException(onFulfilled, this.resolvedValue)	
					})


					// 同步 ｜ 异步 ｜ 链式调用 => 收集失败回调
					this.asyncRejectCallbacks.push(() => {
						catchThenException(onRejected, this.rejectedReason)	
					})

				}
			}, 0)

		})

		return newThenPromise
		
	}


	/**
	 * 类型：prototype方法-catch
	 * 作用： 
	 * 1. 捕获之前函数运行中的reject异常
	 * 2. 支持链式调用
	 * 
	 *
	 ***/
	catch(errorCb) {
		return this.then(null, errorCb)
	}

	/**
	 * 类型：prototype方法-finaly
	 * 特性： 
	 * 1. 无论成功或者失败，都会调用
	 * 2. 支持链式调用then方法
	 * 3. finaly 方法中可以返回一个任意类型的值，如果返回为异步执行结果，则需要等待执行完成，才能继续向下执行
	 ***/
	finaly(callback) {
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => { throw reason })
		})
	}

	/**
	 * 类型：iterable方法-all
	 * 描述： 
	 * 1. 当输入的所有promise的resolve回调都结束，返回一个数组类型的结果集
	 * 2. 当输入的所有promise中的任意一个reject回调执行，或者输入不合法的promise就会立即抛出错误，并且reject的是第一个抛出的错误信息
	 * 3. 支持链式调用then方法
	 * 输入：
	 * Array，Map，Set 等iterable类型
	 ***/
	static all(array) {
		const resolveResult = [] // all方法返回的结果集

		return new MyPromise((resolve, reject) => {
			const setResolveResult = (index, value) => {
				resolveResult[index] = value
				// console.log('+++resolveResult+++', resolveResult)

				//  异步函数会存在空值的情况
				const effectiveResult = resolveResult.filter(effctive => {
				 	if(effctive) return effctive
				})

				// 等待所有参入参数都有确定的返回结果
				effectiveResult.length === array.length && resolve(resolveResult)
			}

			for(let i = 0; i<array.length; i++) {
				const curArgument = array[i]

				// 如果是Promise，则执行当前promise拿到返回结果
				if(curArgument instanceof MyPromise) {
					curArgument.then(value => setResolveResult(i, value), reject)
				} else {
					setResolveResult(i, curArgument)
				}
			}
		})
	}

	/**
	 * 类型：iterable方法
	 * 作用： 
	 * 1. 方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise，并带有一个对象数组，每个对象表示对应的promise结果
	 *
	 ***/
	static allSettled(array) {
		const resolveResult = []

		return new MyPromise((resolve, reject) => {
			const setEachResult = (index, value) => {
				resolveResult[index] = value

				//  异步函数会存在空值的情况
				const effectiveResult = resolveResult.filter(effctive => {
				 	if(effctive) return effctive
				})

				// 等待所有参入参数都有确定的返回结果
				effectiveResult.length === array.length && resolve(resolveResult)
			}

			for (let i = 0; i < array.length; i++) {
				const curArgument = array[i]

				if(curArgument instanceof MyPromise) {
					curArgument.then(value => {
						setEachResult(i, value)
					}, reason => {
						setEachResult(i, reason)
					})
				} else {
					setEachResult(i, curArgument)
				}
			}
		})
	}

	/**
	 * 类型：iterable方法-any
	 * 特性： 
	 * 1. 返回的是第一个成功的值。这个方法将会忽略掉所有被拒绝的promise，直到第一个 promise 成功
	 * 2. 如果传入的参数是一个空的可迭代对象，则返回一个 已失败（already rejected） 状态的 Promise
	 * 3. 如果传入的参数不包含任何 promise，则返回一个 异步完成的promise
	 * 4. 其他情况下都会返回一个处理中（pending） 的 Promise。 只要传入的迭代对象中的任何一个 promise 变成成功（resolve）状态，或者其中的所有的 promises 都失败，那么返回的 promise 就会 异步地（当调用栈为空时） 变成成功/失败（resolved/reject）状态
	 *
	 ***/
	static any(array) {

		return new MyPromise((resolve, reject) => {
			let setEffectvalue = null;
			let rejectCount = 0
			const effctivePromise = array.filter(cur => {
				if(cur instanceof MyPromise) return cur
			})


			// 传入非Promise 参数，则需特殊处理
			if(array.length === 0) {
				reject('No Arguments is Effctive')
				return
			} else if(effctivePromise.length === 0) {
				resolve(array)
				return
			}

			
			for (let i = 0; i < effctivePromise.length; i++) {
				// 返回第一个成功的结果，不在乎其它传入promise的执行状态
				if(setEffectvalue) break;

				effctivePromise[i].then(resolveValue => {
					setEffectvalue = resolveValue

					resolve(setEffectvalue)
				}, reason => {
					rejectCount++

					// 如果所有Promise全部执行失败，则返回执行异常
					rejectCount === effctivePromise.length && reject('AggregateError: No Promise in Promise.any was resolved')
				})
			}
		})
	}

	/**
	 * 类型：iterable方法-race
	 * 描述： 
	 * 1. 返回一个 Promise，它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成（resolves），也可以是失败（rejects），这要取决于第一个完成的方式是两个中的哪个
	 * 2. 如果传的迭代是空的，则返回的 promise 将永远等待
	 *
	 ***/
	static race(array) {
		let isEffectValue = null

		return new Promise((resolve, reject) => {
			if(array.length === 0) return

			for (let i = 0; i < array.length; i++) {
				if(isEffectValue) break 

				const cur = array[i]

				if(cur instanceof MyPromise) {
					cur.then(value => {
						isEffectValue = value

						resolve(isEffectValue)
					}, reason => {
						isEffectValue = reason

						reject(isEffectValue)
					})
				}
			}
		})
	}

	/**
	 * 类型：iterable方法-reject
	 * 作用： 
	 * 1. 返回一个带有拒绝原因的Promise对象
	 * 2. 如果入参是普通值，则包装成promise对象；如果是Promise对象则直接返回
	 * 3. 支持链式调用then方法
	 *
	 ***/
	static reject(reason) {
		if(reason instanceof MyPromise) return reason
		return new MyPromise((resolve, reject) => reject(reason))
	}

	/**
	 * 类型：iterable方法-resolve
	 * 作用： 
	 * 1. 将给定值转换为Promise对象
	 * 2. 如果入参是普通值，则包装成promise对象；如果是Promise对象则直接返回
	 * 3. 支持链式调用then方法
	 *
	 ***/
	static resolve(value) {
		if(value instanceof MyPromise) return value
		return new MyPromise(resolve => resolve(value))
	}
}


module.exports = {
	MyPromise
}




